package com.hmdp.utils.Impl;

import cn.hutool.core.io.resource.ClassPathResource;
import com.hmdp.utils.SimpleLock;
import org.springframework.core.io.Resource;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;

import java.util.Collections;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

public class SimpleLockImpl implements SimpleLock {

    private String name;
    private StringRedisTemplate stringRedisTemplate;
    private String pre_Key = "lock:";
    //为了防止不同线程之间的锁误删的情况，添加锁标识，进行锁判断
    private static final String pre_Id = UUID.randomUUID()+"-";
    private static final DefaultRedisScript<Long> UNLOCK_SCRIPT  ;

    static {
        UNLOCK_SCRIPT = new DefaultRedisScript<>();
        UNLOCK_SCRIPT.setResultType(Long.class);
        UNLOCK_SCRIPT.setLocation((Resource) new ClassPathResource("unlock.lua"));
    }

    public SimpleLockImpl(String name, StringRedisTemplate stringRedisTemplate) {
        this.name = name;
        this.stringRedisTemplate = stringRedisTemplate;
    }

    /**
     * 获取锁
     * @param time
     * @return
     */
    @Override
    public Boolean tryLock(Long time) {
        //1.先获取线程的唯一id
        String threadId = pre_Id+Thread.currentThread().getId();
        //2.获取锁
        Boolean absent = stringRedisTemplate.opsForValue().setIfAbsent(pre_Key + name, threadId + "", time, TimeUnit.SECONDS);
        //因为是包装类的原因在解包的过程当中，可能会出现null值，如果出现null就会导致空指针异常，使用TRUE.equals(absent)进行判断，如果返回值是null那么就会直接返回false
        return absent.TRUE.equals(absent);
    }

    /**
     * 释放锁
     */
    @Override
    public void unlock() {
        //调用lua脚本  ;  第一个传入的参数是key 第二个则是进行验证的value
        stringRedisTemplate.execute(UNLOCK_SCRIPT, Collections.singletonList(pre_Key + name), pre_Id+Thread.currentThread().getId());
    }

}
